using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MongoDB.Bson;

namespace MongoScienceDAL.Core.Tests
{
    [TestClass]
    public class QueryCriteriaHelperMethodTests
    {
        [TestMethod]
        public void ExtensionMethodTextContains()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextContains("t1", "test");

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t1");
            Assert.AreEqual(filter.Type, QueryFilterType.TextContains);

            Assert.AreEqual(filter.Data["text"], "test");
        }

        [TestMethod]
        public void ExtensionMethodTextStartsWith()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextStartsWith("t1", "test");

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t1");
            Assert.AreEqual(filter.Type, QueryFilterType.TextStartsWith);

            Assert.AreEqual(filter.Data["text"], "test");
        }

        [TestMethod]
        public void ExtensionMethodTextEndsWith()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextEndsWith("t1", "test");

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t1");
            Assert.AreEqual(filter.Type, QueryFilterType.TextEndsWith);

            Assert.AreEqual(filter.Data["text"], "test");
        }

        [TestMethod]
        public void ExtensionMethodTextEquals()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextEquals("t1", "test");

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t1");
            Assert.AreEqual(filter.Type, QueryFilterType.TextEquals);

            Assert.AreEqual(filter.Data["text"], "test");
        }

        [TestMethod]
        public void ExtensionMethodTextRegex()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextRegex("t1", "test");

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t1");
            Assert.AreEqual(filter.Type, QueryFilterType.TextRegex);

            Assert.AreEqual(filter.Data["text"], "test");
        }

        [TestMethod]
        public void ExtensionMethodHasField()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddHasField("field1");

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.HasField);
        }

        [TestMethod]
        public void ExtensionMethodDosentHaveField()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDosentHaveField("field1");

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DosentHaveField);
        }

        [TestMethod]
        public void ExtensionMethodDateEquals()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateEquals("field1", DateTime.Today);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DateEquals);

            Assert.AreEqual((DateTime)filter.Data["date"], DateTime.Today);
        }

        [TestMethod]
        public void ExtensionMethodDateGreaterThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateGreaterThan("field1", DateTime.Today);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DateGreaterThan);

            Assert.AreEqual((DateTime)filter.Data["date"], DateTime.Today);
        }

        [TestMethod]
        public void ExtensionMethodDateGreaterThanOrEqualTo()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateGreaterThanOrEqualTo("field1", DateTime.Today);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DateGreaterThanOrEquals);

            Assert.AreEqual((DateTime)filter.Data["date"], DateTime.Today);
        }

        [TestMethod]
        public void ExtensionMethodDateLessThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateLessThan("field1", DateTime.Today);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DateLessThan);

            Assert.AreEqual((DateTime)filter.Data["date"], DateTime.Today);
        }

        [TestMethod]
        public void ExtensionMethodDateLessThanOrEqualTo()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateLessThanOrEqualTo("field1", DateTime.Today);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DateLessThanOrEquals);

            Assert.AreEqual((DateTime)filter.Data["date"], DateTime.Today);
        }

        [TestMethod]
        public void ExtensionMethodDateInsideRange()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateInsideRange("field1", DateTime.Today,DateTime.Today.AddDays(5));

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DateInsideRange);

            Assert.AreEqual((DateTime)filter.Data["startdate"], DateTime.Today);
            Assert.AreEqual((DateTime)filter.Data["enddate"], DateTime.Today.AddDays(5));
        }

        [TestMethod]
        public void ExtensionMethodDateOutsideRange()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateOutsideRange("field1", DateTime.Today, DateTime.Today.AddDays(5));

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "field1");
            Assert.AreEqual(filter.Type, QueryFilterType.DateOutsideRange);

            Assert.AreEqual((DateTime)filter.Data["startdate"], DateTime.Today);
            Assert.AreEqual((DateTime)filter.Data["enddate"], DateTime.Today.AddDays(5));
        }

        [TestMethod]
        public void ExtensionMethodValueEquals()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueEquals("t2", 5);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t2");
            Assert.AreEqual(filter.Type, QueryFilterType.ValueEquals);

            Assert.AreEqual((double)filter.Data["value"], 5);
        }

        [TestMethod]
        public void ExtensionMethodValueGreaterThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueGreaterThan("t2", 5);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t2");
            Assert.AreEqual(filter.Type, QueryFilterType.ValueGreaterThan);

            Assert.AreEqual((double)filter.Data["value"], 5);
        }

        [TestMethod]
        public void ExtensionMethodValueGreaterThanOrEqualTo()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueGreaterThanOrEqualTo("t2", 5);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t2");
            Assert.AreEqual(filter.Type, QueryFilterType.ValueGreaterThanOrEquals);

            Assert.AreEqual((double)filter.Data["value"], 5);
        }

        [TestMethod]
        public void ExtensionMethodValueLessThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueLessThan("t2", 5);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t2");
            Assert.AreEqual(filter.Type, QueryFilterType.ValueLessThan);

            Assert.AreEqual((double)filter.Data["value"], 5);
        }

        [TestMethod]
        public void ExtensionMethodValueLessThanOrEqualTo()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueLessThanOrEqualTo("t2", 5);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t2");
            Assert.AreEqual(filter.Type, QueryFilterType.ValueLessThanOrEquals);

            Assert.AreEqual((double)filter.Data["value"], 5);
        }

        [TestMethod]
        public void ExtensionMethodObjectIdEquals()
        {
            var testId = ObjectId.GenerateNewId();
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddObjectIdEquals(testId);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "_id");
            Assert.AreEqual(filter.Type, QueryFilterType.ObjectIdEquals);

            Assert.AreEqual(filter.Data["value"], testId);
        }

        [TestMethod]
        public void ExtensionMethodObjectIdEqualsAny()
        {
            var testIds = new List<ObjectId>
                             {ObjectId.GenerateNewId(), ObjectId.GenerateNewId(), ObjectId.GenerateNewId()};
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddObjectIdEqualsAny(testIds);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "_id");
            Assert.AreEqual(filter.Type, QueryFilterType.ObjectIdEqualsAny);

            Assert.AreEqual(((List<ObjectId>) filter.Data["value"])[0], testIds[0]);
            Assert.AreEqual(((List<ObjectId>) filter.Data["value"])[1], testIds[1]);
            Assert.AreEqual(((List<ObjectId>) filter.Data["value"])[2], testIds[2]);
        }

        [TestMethod]
        public void ExtensionMethodParentIdEquals()
        {
            var testId = ObjectId.GenerateNewId();
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddParentIdEquals("t9",testId);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t9");
            Assert.AreEqual(filter.Type, QueryFilterType.ParentIdEquals);

            Assert.AreEqual(filter.Data["value"], testId);
        }

        [TestMethod]
        public void ExtensionMethodParentIdEqualsAny()
        {
            var testIds = new List<ObjectId> { ObjectId.GenerateNewId(), ObjectId.GenerateNewId(), ObjectId.GenerateNewId() };
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddParentIdEqualsAny("t9",testIds);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t9");
            Assert.AreEqual(filter.Type, QueryFilterType.ParentIdEqualsAny);

            Assert.AreEqual(((List<ObjectId>)filter.Data["value"])[0], testIds[0]);
            Assert.AreEqual(((List<ObjectId>)filter.Data["value"])[1], testIds[1]);
            Assert.AreEqual(((List<ObjectId>)filter.Data["value"])[2], testIds[2]);
        }

        [TestMethod]
        public void ExtensionMethodChildIdsContain()
        {
            var testId = ObjectId.GenerateNewId();
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddChildIdsContain("t9",testId);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t9");
            Assert.AreEqual(filter.Type, QueryFilterType.ChildIdsContain);

            Assert.AreEqual(filter.Data["value"], testId);
        }

        [TestMethod]
        public void ExtensionMethodChildIdsContainAny()
        {
            var testIds = new List<ObjectId> { ObjectId.GenerateNewId(), ObjectId.GenerateNewId(), ObjectId.GenerateNewId() };
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddChildIdsContainAny("t9",testIds);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t9");
            Assert.AreEqual(filter.Type, QueryFilterType.ChildIdsContainAny);

            Assert.AreEqual(((List<ObjectId>)filter.Data["value"])[0], testIds[0]);
            Assert.AreEqual(((List<ObjectId>)filter.Data["value"])[1], testIds[1]);
            Assert.AreEqual(((List<ObjectId>)filter.Data["value"])[2], testIds[2]);
        }

        [TestMethod]
        public void ExtensionMethodBooleanEquals()
        {
            
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddBooleanEquals("t9", true);

            Assert.AreEqual(queryCriteria.Filters.Count, 1);

            var filter = queryCriteria.Filters[0];

            Assert.AreEqual(filter.Key, "t9");
            Assert.AreEqual(filter.Type, QueryFilterType.BooleanEquals);

            Assert.AreEqual(filter.Data["value"], true);
        }

        [TestMethod]
        public void ExtensionMethodAddSelectorAddsCorrectly()
        {
            var queryCriteria = new QueryCriteria();
            
            queryCriteria.AddSelector("hamster");
            queryCriteria.AddSelector("moose");

            Assert.AreEqual(2, queryCriteria.Selectors.Count);
            Assert.AreEqual("hamster", queryCriteria.Selectors[0].Key);
            Assert.AreEqual("moose", queryCriteria.Selectors[1].Key);
        }

        [TestMethod]
        public void QueryCriteriaShouldStartWithSelectorsEmpty()
        {
            var queryCriteria = new QueryCriteria();

            //should be empty
            Assert.IsNotNull(queryCriteria.Selectors);
            Assert.AreEqual(0, queryCriteria.Selectors.Count);
        }

        [TestMethod]
        public void ExtensionMethodAddSelectorIngoresDuplicateAndColllectionAndIdKeys()
        {
            var queryCriteria = new QueryCriteria();

            Assert.AreEqual(0, queryCriteria.Selectors.Count);

            //should ignore _id and _col
            queryCriteria.AddSelector("_id");
            queryCriteria.AddSelector("_col");

            Assert.AreEqual(0, queryCriteria.Selectors.Count);

            //should only add once
            queryCriteria.AddSelector("hamster");
            queryCriteria.AddSelector("hamster");

            Assert.AreEqual(1, queryCriteria.Selectors.Count);
            Assert.AreEqual("hamster", queryCriteria.Selectors[0].Key);
        }
    }
}